home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / GLUT-3.7 / PROGS / TIFF / scalebias.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  12.4 KB  |  483 lines

  1.  
  2. /* Copyright (c) Mark J. Kilgard, 1997. */
  3.  
  4. /* This program is freely distributable without licensing fees and is
  5.    provided without guarantee or warrantee expressed or implied. This
  6.    program is -not- in the public domain. */
  7.  
  8. /* X compile line: cc -o scalebias scalebias.c -ltiff -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
  9.  
  10. /* This program requires Sam Leffler's libtiff library and GLUT. */
  11.  
  12. /* scalebias demonstrates how an "in place" scale & bias of pixels in the
  13.    frame buffer can often be accomplished faster with OpenGL blending
  14.    extensions instead of using the naive glCopyPixels with glPixelTransfer
  15.    used to do the scale and bias.
  16.  
  17.    The blending approach requires the "blend subtract" EXT extension in
  18.    order to perform negative biases.  You could use this approach without
  19.    the "blend subtract" extension if you never need to do negative
  20.    biases.
  21.  
  22.    NOTE: This blending approach does not allow negative scales.  The
  23.    blending approach also fails if the partial scaling or biasing results
  24.    leave the 0.0 to 1.0 range (example, scale=5.47, bias=-1.2).
  25.  
  26.    This technique can be valuable when you want to perform post-texture
  27.    filtering scaling and biasing (say for volume rendering or image processing),
  28.    but your hardware lacks texture lookup tables.
  29.  
  30.    To give you an idea of the speed advantage of this "in place" blending
  31.    technique for doing scales and biases, on an SGI O2, this program
  32.    runs 8 to 40 times faster with a greater than 1.0 scaling factor when
  33.    using the blending mode instead of using glCopyPixels.  The performance
  34.    improvement depends on the number of pixels scaled or biased. */
  35.  
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include <math.h>
  39. #include <GL/glut.h>
  40. #include <tiffio.h>     /* Sam Leffler's libtiff library. */
  41.  
  42. TIFFRGBAImage img;
  43. uint32 *raster, *texture;
  44. tsize_t npixels;
  45. int imgwidth, imgheight;
  46. int tw, th;
  47.  
  48. int hasABGR = 0, hasBlendSubtract = 0;
  49. int doubleBuffer = 1;
  50. char *filename = NULL;
  51. int ax = 10, ay = -10;
  52. int luminance = 0;
  53. int useBlend = 1;
  54. int timing = 0;
  55. int height;
  56.  
  57. GLfloat scale = 1.0, bias = 0.0, zoom = 1.0;
  58.  
  59. void
  60. reshape(int w, int h)
  61. {
  62.   glViewport(0, 0, w, h);
  63.   glMatrixMode(GL_PROJECTION);
  64.   glLoadIdentity();
  65.   gluOrtho2D(0, w, 0, h);
  66.   glMatrixMode(GL_MODELVIEW);
  67.   glLoadIdentity();
  68.   height = h;
  69. }
  70.  
  71. void
  72. drawImage(void)
  73. {
  74.   glPushMatrix();
  75.   glTranslatef(ax, -ay + imgheight * zoom, 0);
  76.   glScalef(zoom * imgwidth, zoom * imgheight, 1);
  77.   glBegin(GL_QUADS);
  78.   glTexCoord2i(0, 0);
  79.   glVertex2i(0, 0);
  80.   glTexCoord2i(1, 0);
  81.   glVertex2i(1, 0);
  82.   glTexCoord2i(1, -1);
  83.   glVertex2i(1, -1);
  84.   glTexCoord2i(0, -1);
  85.   glVertex2i(0, -1);
  86.   glEnd();
  87.   glPopMatrix();
  88. }
  89.  
  90. void
  91. display(void)
  92. {
  93.   int start, end;
  94.  
  95.   /* Clear the color buffer. */
  96.   glClear(GL_COLOR_BUFFER_BIT);
  97.  
  98.   glColor3f(1.0, 1.0, 1.0);  /* Modulate texture with white. */
  99.   glEnable(GL_TEXTURE_2D);
  100.   drawImage();
  101.  
  102.   if (timing) {
  103.     /* Avoid timing the clear and original draw image speed. */
  104.     glFinish();
  105.     start = glutGet(GLUT_ELAPSED_TIME);
  106.   }
  107.  
  108.   /* Scale and bias via . */
  109.   if (bias != 0.0 || scale != 1.0) {
  110.     glDisable(GL_TEXTURE_2D);
  111.  
  112.     /* Other things you might want to make sure are disabled. */
  113.     /* glDisable(GL_LIGHTING); */
  114.     /* glDisable(GL_DEPTH_TEST); */
  115.  
  116.     if (useBlend && hasBlendSubtract) {
  117.  
  118.       /* NOTE: The blending approach does not allow negative
  119.          scales.  The blending approach also fails if the
  120.          partial scaling or biasing results leave the 0.0 to
  121.          1.0 range (example, scale=5.47, bias=-1.2). */
  122.  
  123.       glEnable(GL_BLEND);
  124.       if (scale > 1.0) {
  125.         float remainingScale;
  126.  
  127.         remainingScale = scale;
  128. #ifdef GL_EXT_blend_subtract
  129.         glBlendEquationEXT(GL_FUNC_ADD_EXT);
  130. #endif
  131.         glBlendFunc(GL_DST_COLOR, GL_ONE);
  132.         if (remainingScale > 2.0) {
  133.           /* Clever cascading approach.  Example: if the
  134.              scaling factor was 9.5, do 3 "doubling" blends
  135.              (8x), then scale by the remaining 1.1875. */
  136.           glColor4f(1, 1, 1, 1);
  137.           while (remainingScale > 2.0) {
  138.             drawImage();
  139.             remainingScale /= 2.0;
  140.           }
  141.         }
  142.         glColor4f(remainingScale - 1,
  143.           remainingScale - 1, remainingScale - 1, 1);
  144.         drawImage();
  145.         glBlendFunc(GL_ONE, GL_ONE);
  146.         if (bias != 0) {
  147.           if (bias > 0) {
  148.             glColor4f(bias, bias, bias, 0.0);
  149.           } else {
  150. #ifdef GL_EXT_blend_subtract
  151.             glBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
  152. #endif
  153.             glColor4f(-bias, -bias, -bias, 0.0);
  154.           }
  155.           drawImage();
  156.         }
  157.       } else {
  158.         if (bias > 0) {
  159. #ifdef GL_EXT_blend_subtract
  160.           glBlendEquationEXT(GL_FUNC_ADD_EXT);
  161. #endif
  162.           glColor4f(bias, bias, bias, scale);
  163.         } else {
  164. #ifdef GL_EXT_blend_subtract
  165.           glBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
  166. #endif
  167.           glColor4f(-bias, -bias, -bias, scale);
  168.         }
  169.         glBlendFunc(GL_ONE, GL_SRC_ALPHA);
  170.         drawImage();
  171.       }
  172.       glDisable(GL_BLEND);
  173.     } else {
  174.       glPixelTransferf(GL_RED_SCALE, scale);
  175.       glPixelTransferf(GL_GREEN_SCALE, scale);
  176.       glPixelTransferf(GL_BLUE_SCALE, scale);
  177.       glPixelTransferf(GL_RED_BIAS, bias);
  178.       glPixelTransferf(GL_GREEN_BIAS, bias);
  179.       glPixelTransferf(GL_BLUE_BIAS, bias);
  180.       glRasterPos2i(0, 0);
  181.       glBitmap(0, 0, 0, 0, ax, -ay, NULL);
  182.       glCopyPixels(ax, -ay,
  183.         ceil(imgwidth * zoom), ceil(imgheight * zoom), GL_COLOR);
  184.       glPixelTransferf(GL_RED_SCALE, 1.0);
  185.       glPixelTransferf(GL_GREEN_SCALE, 1.0);
  186.       glPixelTransferf(GL_BLUE_SCALE, 1.0);
  187.       glPixelTransferf(GL_RED_BIAS, 0.0);
  188.       glPixelTransferf(GL_GREEN_BIAS, 0.0);
  189.       glPixelTransferf(GL_BLUE_BIAS, 0.0);
  190.     }
  191.   }
  192.   if (timing) {
  193.     glFinish();
  194.     end = glutGet(GLUT_ELAPSED_TIME);
  195.     printf("time = %d milliseconds\n", end - start);
  196.   }
  197.   /* Swap the buffers if necessary. */
  198.   if (doubleBuffer) {
  199.     glutSwapBuffers();
  200.   } else {
  201.     glFlush();
  202.   }
  203. }
  204.  
  205. static int moving = 0, ox, oy;
  206.  
  207. void
  208. mouse(int button, int state, int x, int y)
  209. {
  210.   if (button == GLUT_LEFT_BUTTON) {
  211.     if (state == GLUT_DOWN) {
  212.  
  213.       /* Left mouse button press.  Update last seen mouse
  214.          position. And set "moving" true since button is
  215.          pressed. */
  216.       ox = x;
  217.       oy = y;
  218.       moving = 1;
  219.  
  220.     } else {
  221.  
  222.       /* Left mouse button released; unset "moving" since
  223.          button no longer pressed. */
  224.       moving = 0;
  225.  
  226.     }
  227.   }
  228. }
  229.  
  230. void
  231. motion(int x, int y)
  232. {
  233.   /* If there is mouse motion with the left button held down. */
  234.   if (moving) {
  235.  
  236.     /* Figure out offset from the last mouse position seen. */
  237.     ax += (x - ox);
  238.     ay += (y - oy);
  239.  
  240.     /* Request a window redraw. */
  241.     glutPostRedisplay();
  242.  
  243.     /* Update last seen mouse position. */
  244.     ox = x;
  245.     oy = y;
  246.   }
  247. }
  248.  
  249. void
  250. updateTitle(void)
  251. {
  252.   char title[200];
  253.  
  254.   sprintf(title, "Scale (%.2f) & Bias (%.1f) via %s", scale, bias,
  255.     useBlend ? "Blend" : "Copy");
  256.   glutSetWindowTitle(title);
  257. }
  258.  
  259. void
  260. option(int value)
  261. {
  262.   switch (value) {
  263.   case 6:
  264.     bias += 0.1;
  265.     break;
  266.   case 7:
  267.     bias -= 0.1;
  268.     break;
  269.   case 8:
  270.     scale *= 1.1;
  271.     break;
  272.   case 9:
  273.     scale *= 0.9;
  274.     break;
  275.   case 10:
  276.     scale = 1.0;
  277.     bias = 0.0;
  278.     break;
  279.   case 11:
  280.     if (hasBlendSubtract) {
  281.       useBlend = 1 - useBlend;
  282.     }
  283.     break;
  284.   case 12:
  285.     zoom += 0.2;
  286.     break;
  287.   case 13:
  288.     zoom -= 0.2;
  289.     break;
  290.   case 14:
  291.     timing = 1 - timing;
  292.     break;
  293.   case 666:
  294.     exit(0);
  295.     break;
  296.   }
  297.   updateTitle();
  298.   glutPostRedisplay();
  299. }
  300.  
  301. /* ARGSUSED1 */
  302. void
  303. special(int key, int x, int y)
  304. {
  305.   switch (key) {
  306.   case GLUT_KEY_UP:
  307.     option(6);
  308.     break;
  309.   case GLUT_KEY_DOWN:
  310.     option(7);
  311.     break;
  312.   case GLUT_KEY_LEFT:
  313.     option(9);
  314.     break;
  315.   case GLUT_KEY_RIGHT:
  316.     option(8);
  317.     break;
  318.   case GLUT_KEY_HOME:
  319.     option(10);
  320.     break;
  321.   case GLUT_KEY_INSERT:
  322.     option(11);
  323.     break;
  324.   case GLUT_KEY_PAGE_UP:
  325.     option(12);
  326.     break;
  327.   case GLUT_KEY_PAGE_DOWN:
  328.     option(13);
  329.     break;
  330.   }
  331. }
  332.  
  333. int
  334. main(int argc, char **argv)
  335. {
  336.   TIFF *tif;
  337.   char emsg[1024];
  338.   int i;
  339.  
  340.   glutInit(&argc, argv);
  341.   for (i = 1; i < argc; i++) {
  342.     if (!strcmp(argv[i], "-sb")) {
  343.       doubleBuffer = 0;
  344.     } else {
  345.       filename = argv[i];
  346.     }
  347.   }
  348.   if (filename == NULL) {
  349.     fprintf(stderr, "usage: scalebias [GLUT-options] [-sb] TIFF-file\n");
  350.     exit(1);
  351.   }
  352.   tif = TIFFOpen(filename, "r");
  353.   if (tif == NULL) {
  354.     fprintf(stderr, "Problem showing %s\n", filename);
  355.     exit(1);
  356.   }
  357.   if (TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
  358.     npixels = (tsize_t) (img.width * img.height);
  359.     raster = (uint32 *) _TIFFmalloc(npixels * (tsize_t) sizeof(uint32));
  360.     if (raster != NULL) {
  361.       if (TIFFRGBAImageGet(&img, raster, img.width, img.height) == 0) {
  362.         TIFFError(filename, emsg);
  363.         exit(1);
  364.       }
  365.     }
  366.     TIFFRGBAImageEnd(&img);
  367.   } else {
  368.     TIFFError(filename, emsg);
  369.     exit(1);
  370.   }
  371.   if (doubleBuffer) {
  372.     glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
  373.   } else {
  374.     glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
  375.   }
  376.   imgwidth = (int) img.width;
  377.   imgheight = (int) img.height;
  378.   glutInitWindowSize(imgwidth * 1.5, imgheight * 1.5);
  379.   glutCreateWindow("");
  380.   glutReshapeFunc(reshape);
  381.   glutDisplayFunc(display);
  382.   glutMouseFunc(mouse);
  383.   glutMotionFunc(motion);
  384.   glutSpecialFunc(special);
  385. #ifdef GL_EXT_abgr
  386.   if (glutExtensionSupported("GL_EXT_abgr")) {
  387.     hasABGR = 1;
  388.   }
  389. #endif
  390. #ifdef GL_EXT_blend_subtract
  391.   if (glutExtensionSupported("GL_EXT_blend_subtract")) {
  392.     hasBlendSubtract = 1;
  393.   }
  394. #endif
  395.   if (!hasBlendSubtract) {
  396.     printf("\nThis program needs the blend subtract extension for\n");
  397.     printf("fast blending-base in-place scaling & biasing.  Since\n");
  398.     printf("the extension is not available, using the slower\n");
  399.     printf("glCopyPixels approach.\n\n");
  400.     useBlend = 0;
  401.   }
  402.   /* If cannot directly display ABGR format, we need to reverse
  403.      the component ordering in each pixel. :-( */
  404.   if (!hasABGR) {
  405.     int i;
  406.  
  407.     for (i = 0; i < npixels; i++) {
  408.       register unsigned char *cp = (unsigned char *) &raster[i];
  409.       int t;
  410.  
  411.       t = cp[3];
  412.       cp[3] = cp[0];
  413.       cp[0] = t;
  414.       t = cp[2];
  415.       cp[2] = cp[1];
  416.       cp[1] = t;
  417.     }
  418.   }
  419.   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  420.  
  421.   /* Linear sampling within a mipmap level. */
  422.   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
  423.     GL_LINEAR_MIPMAP_NEAREST);
  424.  
  425.   /* A TIFF file could be any size; OpenGL textures are allowed
  426.      to have a width and height that is a power of two (32, 64,
  427.      128, etc.). To maximize the use of available texture
  428.      memory, we scale the image to gluScaleImage to the next
  429.      larger power of 2 width or height dimension (not exceeding
  430.      512, don't want to use too much texture memory!).  This
  431.      rescaling can result in a bit of image bluring because of
  432.      the resampling done by gluScaleImage.  An alternative would
  433.      be to change the texture coordinates  to only use a portion
  434.      texture area. */
  435.  
  436.   tw = 1 << (int) ceil(log(img.width) / log(2.0));
  437.   th = 1 << (int) ceil(log(img.height) / log(2.0));
  438.   if (tw > 512)
  439.     tw = 512;
  440.   if (th > 512)
  441.     th = 512;
  442.   texture = (uint32 *) malloc(sizeof(GLubyte) * 4 * tw * th);
  443.  
  444. #ifdef GL_EXT_abgr
  445. #define APPROPRIATE_FORMAT (hasABGR ? GL_ABGR_EXT : GL_RGBA)
  446. #else
  447. #define APPROPRIATE_FORMAT GL_RGBA
  448. #endif
  449.  
  450.   gluScaleImage(APPROPRIATE_FORMAT,
  451.     (GLsizei) img.width, (GLsizei) img.height, GL_UNSIGNED_BYTE, raster,
  452.     tw, th, GL_UNSIGNED_BYTE, texture);
  453.  
  454.   /* Build mipmaps for the texture image.  Since we are not
  455.      scaling the image (we easily could by calling glScalef),
  456.      creating mipmaps is not really useful, but it is done just
  457.      to show how easily creating mipmaps is. */
  458.   gluBuild2DMipmaps(GL_TEXTURE_2D, 4, tw, th,
  459.     APPROPRIATE_FORMAT, GL_UNSIGNED_BYTE,
  460.     texture);
  461.  
  462.   glutCreateMenu(option);
  463.   glutAddMenuEntry("Increase bias (Up)", 6);
  464.   glutAddMenuEntry("Decrease bias (Down)", 7);
  465.   glutAddMenuEntry("Increase scale (Right)", 8);
  466.   glutAddMenuEntry("Decrease scale (Left)", 9);
  467.   glutAddMenuEntry("Reset scale & bias (Home)", 10);
  468.   if (hasBlendSubtract) {
  469.     glutAddMenuEntry("Toggle blend/copy (Insert)", 11);
  470.   }
  471.   glutAddMenuEntry("Zoom up (PageUp)", 12);
  472.   glutAddMenuEntry("Zoom down (PageDown)", 13);
  473.   glutAddMenuEntry("Toggle timing", 14);
  474.   glutAddMenuEntry("Quit", 666);
  475.   glutAttachMenu(GLUT_RIGHT_BUTTON);
  476.   /* Use a gray background so TIFF images with black
  477.      backgrounds will show against textiff's background. */
  478.   glClearColor(0.2, 0.2, 0.2, 1.0);
  479.   updateTitle();
  480.   glutMainLoop();
  481.   return 0;             /* ANSI C requires main to return int. */
  482. }
  483.